    // Copyright (C) 2003  Davis E. King (davis@dlib.net)
    // License: Boost Software License   See LICENSE.txt for the full license.
    #ifndef DLIB_SEQUENCE_KERNEl_2_
    #define DLIB_SEQUENCE_KERNEl_2_

    #include "sequence_kernel_abstract.h"
    #include "../algs.h"
    #include "../interfaces/enumerable.h"
    #include "../interfaces/remover.h"
    #include "../serialize.h"

    namespace dlib
    {

        template <
            typename T,
            typename mem_manager = default_memory_manager
            >
        class sequence_kernel_2 : public enumerable<T>,
                                  public remover<T>
        {
            /*!
                INITIAL VALUE
                    sequence_size   == 0
                    at_start_       == true
                    current_enumeration_node == 0

                CONVENTION
                    sequence_size == the number of elements in the sequence

                    at_start_ == at_start()
                    (current_enumeration_node!=0) == current_element_valid()
                    if (current_enumeration_node!=0) then
                        current_enumeration_node->item == element()
                        current_enumeration_pos == the position of the node pointed to by
                                                   current_enumeration_node

                    if ( sequence_size > 0 )
                    {
                        current_node        == pointer to a node in the linked list and
                        current_node->right->right->... eventually == current_node and
                        current_node->left->left->... eventually == current_node and
                        current_pos         == the position in the sequence of
                                               current_node->item
                    }

            !*/

            struct node {
                T item;
                node* right;
                node* left;
            };

            public:

                typedef T type;
                typedef mem_manager mem_manager_type;

                sequence_kernel_2 (
                ) :
                    sequence_size(0),
                    at_start_(true),
                    current_enumeration_node(0)
                {}

                virtual ~sequence_kernel_2 (
                );

                inline void clear (
                );

                void add (
                    unsigned long pos,
                    T& item
                );

                void remove (
                    unsigned long pos,
                    T& item
                );

                void cat (
                    sequence_kernel_2& item
                );

                const T& operator[] (
                    unsigned long pos
                ) const;

                T& operator[] (
                    unsigned long pos
                );

                void swap (
                    sequence_kernel_2& item
                );

                // functions from the remover interface
                inline void remove_any (
                    T& item
                );

                // functions from the enumerable interface
                inline size_t size (
                ) const;

                bool at_start (
                ) const;

                inline void reset (
                ) const;

                bool current_element_valid (
                ) const;

                const T& element (
                ) const;

                T& element (
                );

                bool move_next (
                ) const;

            private:

                void delete_nodes (
                    node* current_node,
                    unsigned long sequence_size
                );
                /*!
                    requires
                        CONVENTION IS CORRECT
                    ensures
                        all memory associated with the ring of nodes has been freed
                !*/

                void move_to_pos (
                    node*& current_node,
                    unsigned long& current_pos,
                    unsigned long pos,
                    unsigned long size
                ) const;
                /*!
                    requires
                        everything in the CONVENTION is correct and
                        there is a node corresponding to pos in the CONVENTION and
                        0 <= pos < size
                    ensures
                        current_pos == pos and
                        current_node->item is the item in the sequence associated with
                        position pos
                !*/

                // data members
                unsigned long sequence_size;
                mutable node* current_node;
                mutable unsigned long current_pos;
                mutable bool at_start_;
                mutable node* current_enumeration_node;
                mutable unsigned long current_enumeration_pos;

                // restricted functions
                sequence_kernel_2(sequence_kernel_2&);        // copy constructor
                sequence_kernel_2& operator=(sequence_kernel_2&); // assignment operator

        };

        template <
            typename T,
            typename mem_manager
            >
        inline void swap (
            sequence_kernel_2<T,mem_manager>& a,
            sequence_kernel_2<T,mem_manager>& b
        ) { a.swap(b); }

        template <
            typename T,
            typename mem_manager
            >
        void deserialize (
            sequence_kernel_2<T,mem_manager>& item,
            std::istream& in
        )
        {
            try
            {
                item.clear();
                unsigned long size;
                deserialize(size,in);
                T temp;
                for (unsigned long i = 0; i < size; ++i)
                {
                    deserialize(temp,in);
                    item.add(i,temp);
                }
            }
            catch (serialization_error& e)
            {
                item.clear();
                throw serialization_error(e.info + "\n   while deserializing object of type sequence_kernel_2");
            }
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // member function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        sequence_kernel_2<T,mem_manager>::
        ~sequence_kernel_2 (
        )
        {
            delete_nodes(current_node,sequence_size);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        void sequence_kernel_2<T,mem_manager>::
        clear (
        )
        {
            if (sequence_size != 0)
            {
                delete_nodes(current_node,sequence_size);
                sequence_size = 0;
            }
            // reset the enumerator
            reset();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        void sequence_kernel_2<T,mem_manager>::
        add (
            unsigned long pos,
            T& item
        )
        {
            // make new node and swap item into it
            node* new_node = new node;
            exchange(item,new_node->item);

            if (sequence_size > 0)
            {
                if (pos == sequence_size)
                {
                    move_to_pos(current_node,current_pos,pos-1,sequence_size);

                    node& n_node = *new_node;
                    node& c_node = *current_node;

                    // make new node point to the nodes to its left and right
                    n_node.right = c_node.right;
                    n_node.left  = current_node;

                    // make the left node point back to new_node
                    c_node.right->left = new_node;

                    // make the right node point back to new_node
                    c_node.right = new_node;
                    current_pos = pos;

                }
                else
                {
                    move_to_pos(current_node,current_pos,pos,sequence_size);

                    node& n_node = *new_node;
                    node& c_node = *current_node;

                    // make new node point to the nodes to its left and right
                    n_node.right = current_node;
                    n_node.left  = c_node.left;

                    // make the left node point back to new_node
                    c_node.left->right = new_node;

                    // make the right node point back to new_node
                    c_node.left = new_node;
                }

            }
            else
            {
                current_pos = 0;
                new_node->left = new_node;
                new_node->right = new_node;
            }

            // make the new node the current node
            current_node = new_node;

            ++sequence_size;
            // reset the enumerator
            reset();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        void sequence_kernel_2<T,mem_manager>::
        remove (
            unsigned long pos,
            T& item
        )
        {
            move_to_pos(current_node,current_pos,pos,sequence_size);
            node& c_node = *current_node;
            exchange(c_node.item,item);

            node* temp = current_node;

            // close up gap left by remove
            c_node.left->right = c_node.right;
            c_node.right->left = c_node.left;

            current_node = c_node.right;

            --sequence_size;

            delete temp;
            // reset the enumerator
            reset();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        const T& sequence_kernel_2<T,mem_manager>::
        operator[] (
            unsigned long pos
        ) const
        {
            move_to_pos(current_node,current_pos,pos,sequence_size);
            return current_node->item;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        void sequence_kernel_2<T,mem_manager>::
        cat (
            sequence_kernel_2<T,mem_manager>& item
        )
        {
            if (item.sequence_size > 0)
            {
                if (sequence_size > 0)
                {
                    // move both sequences to a convenient location
                    move_to_pos(current_node,current_pos,0,sequence_size);
                    item.move_to_pos (
                        item.current_node,
                        item.current_pos,
                        item.sequence_size-1,
                        item.sequence_size
                    );

                    // make copies of poitners
                    node& item_right = *item.current_node->right;
                    node& left = *current_node->left;

                    item.current_node->right = current_node;
                    current_node->left = item.current_node;

                    left.right = &item_right;
                    item_right.left = &left;

                    // set sizes
                    sequence_size += item.sequence_size;
                    item.sequence_size = 0;
                }
                else
                {
                    // *this is empty so just swap
                    item.swap(*this);
                }
            }
            item.clear();
            // reset the enumerator
            reset();
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        T& sequence_kernel_2<T,mem_manager>::
        operator[] (
            unsigned long pos
        )
        {
            move_to_pos(current_node,current_pos,pos,sequence_size);
            return current_node->item;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        size_t sequence_kernel_2<T,mem_manager>::
        size (
        ) const
        {
            return sequence_size;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        void sequence_kernel_2<T,mem_manager>::
        swap (
            sequence_kernel_2<T,mem_manager>& item
        )
        {
            unsigned long   sequence_size_temp         = item.sequence_size;
            node*           current_node_temp          = item.current_node;
            unsigned long   current_pos_temp           = item.current_pos;
            bool            at_start_temp              = item.at_start_;
            node*           current_enumeration_node_temp = item.current_enumeration_node;
            unsigned long   current_enumeration_pos_temp = item.current_enumeration_pos;

            item.sequence_size  = sequence_size;
            item.current_node   = current_node;
            item.current_pos    = current_pos;
            item.at_start_      = at_start_;
            item.current_enumeration_node = current_enumeration_node;
            item.current_enumeration_pos = current_enumeration_pos;

            sequence_size   = sequence_size_temp;
            current_node    = current_node_temp;
            current_pos     = current_pos_temp;
            at_start_       = at_start_temp;
            current_enumeration_node = current_enumeration_node_temp;
            current_enumeration_pos = current_enumeration_pos_temp;
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // enumerable function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        bool sequence_kernel_2<T,mem_manager>::
        at_start (
        ) const
        {
            return at_start_;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        void sequence_kernel_2<T,mem_manager>::
        reset (
        ) const
        {
            at_start_ = true;
            current_enumeration_node = 0;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        bool sequence_kernel_2<T,mem_manager>::
        current_element_valid (
        ) const
        {
            return (current_enumeration_node!=0);
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        const T& sequence_kernel_2<T,mem_manager>::
        element (
        ) const
        {
            return current_enumeration_node->item;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        T& sequence_kernel_2<T,mem_manager>::
        element (
        )
        {
            return current_enumeration_node->item;
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        bool sequence_kernel_2<T,mem_manager>::
        move_next (
        ) const
        {
            if (at_start_ && sequence_size>0)
            {
                move_to_pos(current_node,current_pos,0,sequence_size);
                current_enumeration_node = current_node;
                current_enumeration_pos = 0;
            }
            else if (current_enumeration_node!=0)
            {
                ++current_enumeration_pos;
                if (current_enumeration_pos<sequence_size)
                {
                    current_enumeration_node = current_enumeration_node->right;
                }
                else
                {
                    // we have reached the end of the sequence
                    current_enumeration_node = 0;
                }
            }

            at_start_ = false;
            return (current_enumeration_node!=0);
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // remover function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        void sequence_kernel_2<T,mem_manager>::
        remove_any (
            T& item
        )
        {
            remove(0,item);
        }

    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------
        // private member function definitions
    // ----------------------------------------------------------------------------------------
    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        void sequence_kernel_2<T,mem_manager>::
        delete_nodes (
            node* current_node,
            unsigned long sequence_size
        )
        {
            node* temp;
            while (sequence_size)
            {
                temp = current_node->right;
                delete current_node;
                current_node = temp;
                --sequence_size;
            }
        }

    // ----------------------------------------------------------------------------------------

        template <
            typename T,
            typename mem_manager
            >
        void sequence_kernel_2<T,mem_manager>::
        move_to_pos (
            node*& current_node,
            unsigned long& current_pos,
            unsigned long pos,
            unsigned long size
        ) const
        {
            if ( current_pos > pos)
            {
                // number of hops in each direction needed to reach pos
                unsigned long right = size + pos - current_pos;
                unsigned long left = current_pos - pos;
                current_pos = pos;

                if (left < right)
                {
                    // move left to position pos
                    for (; left > 0; --left)
                        current_node = current_node->left;
                }
                else
                {
                    // move left to position pos
                    for (; right > 0; --right)
                        current_node = current_node->right;
                }
            }
            else if (current_pos != pos)
            {
                // number of hops in each direction needed to reach pos
                unsigned long right = pos - current_pos;
                unsigned long left = size - pos + current_pos;
                current_pos = pos;

                if (left < right)
                {
                    // move left to position pos
                    for (; left > 0; --left)
                        current_node = current_node->left;
                }
                else
                {
                    // move left to position pos
                    for (; right > 0; --right)
                        current_node = current_node->right;
                }
            }
        }

    // ----------------------------------------------------------------------------------------

    }

    #endif // DLIB_SEQUENCE_KERNEl_2_